/*
 * gdt.c
 *
 *  Created on: 01/09/2010
 *      Author: Wendell
 *
 * ===== A GDT =====
 *
 * 		Uma parte vital das várias medidas de proteção do 386 é a Tabela de
 * Descritores Global (Global Descriptor Table), de outro modo chamada GDT.
 * A GDT define um base de privilégios de acesso para certas partes da memória.
 * Nós podemos usar uma entrada na GDT para gerar exceções por violação de
 * segmento que dá ao kernel uma oportunidade para finalizar um processo que
 * está fazendo alguma coisa que não deve. A maioria do sistemas operacionais
 * modernos usam um modo de memória chamado "Paginação (Paging)" para isto:
 * Este é muito mais versátil e permite maior flexibilidade.
 * 		A GDT pode também definir se uma seção na memória é executável ou se é
 * de fato, dados. A GDT é também capaz de definir quais Segmentos de Estados
 * de Tarefas (Task State Segments (TSSes)) são chamados. Um TSS é usado em
 * multitarefa baseada em hardware, e não é discutida aqui. Note que um TSS não
 * é o único meio de se habilitar multitarefa.
 *
 * 		Note que o GRUB já instala uma GDT para você, mas se nós sobrescrevermos
 * a área da memória que o GRUB foi carregado, nós vamos jogar fora a GDT e isto
 * vai causar uma 'falta tripla'. Com sorte, isto vai resetar a maquina.
 * 		O que nós faremos para prevenir este problema é atribuir nossa própria
 * GDT em um lugar da memória que nós conhecemos e podemos acessar. Isto envolve
 * construir nossa própria GDT, contar ao processador onde este está, e final-
 * mente carregar os registradoress CS, DS, ES, FS, e GS com nossas novas
 * entradas.
 * 		O registrador CS é também conhecido como o segmento de código
 * (Code Segment). O segmento de código diz ao processador em qual offset na
 * GDT estão localizados os privilégios de acesso para executar o código
 * corrente.
 * 		O registrador DS é a mesma idéia, mas não é para código, é o segmento de
 * dados (Data Segment) e define os privilégios de acesso para os dados
 * correntes.
 * 		ES, FS, e GS são simplesmente registradores de DS alternativos, e não
 * são importantes para nós.
 *
 * 		A GDT em si é uma lista de entradas longas de 64 bits. Essas entradas
 * definem onde na memória a região permitida vai começar, assim como o limite
 * desta região, e os privilégios de acesso associados com esta entrada.
 * 		Uma regra comum é que a primeira entrada na sua GDT, entrada 0, é
 * conhecida como o descritor NULO (NULL descriptor). Sem registro de segmento
 * precisa ser atribuído para 0, senão este irá causar uma Falta Geral de
 * Proteção (General Protection Fault (GPF)), e é um recurso de proteção do
 * processador.
 * 		A Falta Geral de Proteção e vários outros tipos de 'exceções' vão ser
 * explicados com detalhes na seção sobre Rotinas para Serviços de Interrupções
 * (ISRs).
 *
 * 		Cada entrada na GDT também define seja ou não o segmento corrente que o
 * processador está rodando é para uso do Sistema (Ring 0) ou para uso de
 * Aplicações (Ring 3). Existem outros tipos de rings, mas não são importantes.
 * A Maior parte dos sistemas operacionais hoje em dia usa somente o Ring 0 e
 * Ring 3.
 * 		Como uma regra básica, qualquer aplicação causa uma exceção quando tenta
 * acessar os dados do sistema ou Ring 0. Esta proteção existe para prevenir
 * qualquer aplicação de causar o colapso do kernel.
 * 		Até a GDT é interessante, os níveis ring dizem ao processador se são
 * permitidos executar instruções especiais privilegiadas. Certas instruções são
 * privilegiadas, significando que só podem ser executadas em níveis ring altos.
 * Exemplos disto são 'cli' e 'sti' que desabilita ou habilita interrupções,
 * respectivamente.
 * 		Se uma aplicação tem permissão para usar as instruções assembly 'cli' e
 * 'sti', poderiam efetivamente parar de fazer seu kernel rodar. Você vai apren-
 * der mais sobre interrupções na próxima seção deste tutorial.
 *
 * 		Cada campo de entrada de acesso e Granularidade da GDT podem ser definidos
 * como segue:
 *
 * 7 | 6	5 |	4 | 3	 0
 * P    DPL	    DT	 Type
 *
 * P - Segmento está presente? (1 = Sim)
 * DPL - Que Ring (0 para 3)
 * DT - Tipo de Descritor
 * Type - Que tipo?
 *
 * 7 | 6 | 5 | 4 | 3	        0
 * G   D   0   A   Seg Len. 19:16
 *
 * G - Granularidade (0 = 1byte, 1 = 4kbyte)
 * D - Tamanho de Operador (0 = 16bit, 1 = 32-bit)
 * 0 - Sempre 0
 * A - Disponível para Sistema (Sempre atribuído para 0)
 *
 *		Em nosso tutorial sobre kernel, nós vamos criar uma GDT com somente 3
 *	entradas. Por que 3? Nós necessitamos de um descritor 'fictício' no começo
 *	para ser nosso segmento NULO para recursos de proteção de memória do
 *	processador. Essas entradas são para o kernel.
 *		Os demais registros são para os processos. Cada processo terá um
 *	registro.
 *		Nós precisamos de uma entrada para o Segmento de Códigos (Code Segment),
 *  e finalmente, nós precisamos de uma entrada para os registradores de
 *  Segmento de Dados (Data Segment).
 *  	Para dizer ao processador onde está nossa nova tabela GDT, nós usamos o
 *  opcode assembly 'lgdt'. 'lgdt' precisa ter um ponteiro para uma estrutura
 *  especial de 48-bit. Esta estrutura especial de 48-bit é feita de 16-bit para
 *  o limite da GDT (outra vez, necessária para proteger  assim o processador
 *  podendo criar imediatamente uma Falta de Proteção Geral se nós usarmos um
 *  seguimento cujo offset não existe na GDT), e um endereço de 32-bits para a
 *  GDT em si.
 *
 *  	Nós podemos usar simplesmente um arranjo de 3 entradas para definir
 *  nossa GDT. Para nosso ponteiro especial para GDT, nós precisaremos somente
 *  declarar um. Nós chamaremos de 'gp'.
 *
 */

#include <system.h>
#include <memory.h>

void addEntryGDT();

#define GDTSIZE 32
int gdtCount = 0;

/**
 * O nosso GDT, com 3 entradas.
 * e finalmente nosso ponteiro especial de GDT.
 */
struct GDTEntry gdt[GDTSIZE];
struct GDTptr gp;

tss_entry_t tss_entry;

/**
 * Esta funcao está em loader.asm.
 * Usada para recarregar/atualizar os registradores apropriadamente
 */
extern void gdt_flush(uint);
extern void tss_flush();


void set_kernel_stack(uint);

/**
 * 		Até aqui não é o suficiente para atualmente reservar espaço em memória
 * para uma GDT. Nós precisamos escrever valores em cada entrada da GDT,
 * atribuir o ponteiro da GDT 'gp', e então nós precisamos chamar gdt_flush()
 * para executar o update.
 * 		Há uma função especial que segue, chamada 'gdt_set_entry()', que faz
 * todos os deslocamentos para ajustar cada campo na entrada dada da GDT para o
 * valor apropriado usando facilmente os argumentos da função.
 */
//Atribui um descritor na Tabela de Descritores Global(Global Descriptor Table)
void gdtSetGate(int num, unsigned long base, unsigned long limit,
		unsigned char access, unsigned char gran) {
	//Atribui o endereço do descritor base
    gdt[num].baseLow = (base & 0xFFFF);
    gdt[num].baseMiddle = (base >> 16) & 0xFF;
    gdt[num].baseHigh = (base >> 24) & 0xFF;

    //Atribui os limites do descritor
    gdt[num].limitLow = (limit & 0xFFFF);
    gdt[num].granularity = ((limit >> 16) & 0x0F);

    //Finalmente, atribui as flags de granularidade e de acesso
    gdt[num].granularity |= (gran & 0xF0);
    gdt[num].access = access;
}

/**
 * Será chamado pela main(kmain).
 * Ele atribui o ponteiro especial GDT, atribui as primeiras 3 entradas na
 * nossa GDT, e então finalmente chama gdt_flush() em nosso arquivo assembly
*  para chamar o processador dizendo onde nossa nova GDT está e atualizar os
*  novos registradores de segmento.
 */
void gdtInstall() {
	//Atribui o ponteiro GDT e limite
    gp.limit = (sizeof(struct GDTEntry) * GDTSIZE) - 1;
    gp.base = (uint) &gdt;

    //Nosso descritor NULO (NULL).
    //gdtCount inicialmente eh zero
    gdtSetGate(gdtCount++, 0, 0, 0, 0);

    /**
     * A segunda entrada é nosso Segmento de Código (Code Segment).
     * O endereço base é 0, o limite é 4GBytes, e usa granularidade de 4KByte,
    *  usa opcodes de 32 bits, e é um descritor de Segmento de Código.
    *  Checar a tabela acima no tutorial
    *  para ver o que exatamente o que cada valor significa
    *
    *  0x9A = 10011010
    *  Segm = 1, Ring = 00, Tipo de Desc = 1, Tipo =  1010
    */
    gdtSetGate(gdtCount++, 0, 0xFFFFFFFF, 0x9A, 0xCF);

    /**
     * A terceira entrada é nosso Segmento de Dados (Data Segment).
     * Este é EXATAMENTE
     * igual ao nosso segmento de código, mas o tipo de descritor neste
     * byte de entrada diz que este é um Segmento de Dados
     *
     * 0x92 = 10010010
     * Segm = 1, Ring = 00, Tipo de Desc = 1, Tipo =  0010
     */
    gdtSetGate(gdtCount++, 0, 0xFFFFFFFF, 0x92, 0xCF);

    // User mode code segment
    gdtSetGate(gdtCount++, 0x0, 0xFFFFFFFF, 0xFA, 0xCF);

    // User mode data segment
    gdtSetGate(gdtCount++, 0x0, 0xFFFFFFFF, 0xF2, 0xCF);

//    write_tss(gdtCount++, 0x10, 0x0);
      //Descarta a antiga GDT e instala as novas mudanças!
      gdt_flush((uint)&gp);
//      tss_flush();

}


void t() {
	print("willy");
	__asm__ __volatile__ ("sti");
}

//void write_tss(int num, ushort ss0, uint esp0)
//{
//   // Firstly, let's compute the base and limit of our entry into the GDT.
//   uint base = (uint) &tss_entry;
//   uint limit = base + sizeof(tss_entry);
//
//   // Now, add our TSS descriptor's address to the GDT.
//   gdtSetGate(num, base, limit, 0xE9, 0x00);
//
//   // Ensure the descriptor is initially zero.
//   memset(&tss_entry, 0, sizeof(tss_entry));
//
//   tss_entry.ss0  = ss0;  // Set the kernel stack segment.
//   tss_entry.esp0 = esp0; // Set the kernel stack pointer.
//
//   // Here we set the cs, ss, ds, es, fs and gs entries in the TSS. These specify what
//   // segments should be loaded when the processor switches to kernel mode. Therefore
//   // they are just our normal kernel code/data segments - 0x08 and 0x10 respectively,
//   // but with the last two bits set, making 0x0b and 0x13. The setting of these bits
//   // sets the RPL (requested privilege level) to 3, meaning that this TSS can be used
//   // to switch to kernel mode from ring 3.
//   tss_entry.cs   = 0x0b;
//   tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x13;
//}
//
//void set_kernel_stack(uint stack)
//{
//   tss_entry.esp0 = stack;
//}

